package com.northpool.operator.common.minio;

import cn.hutool.core.io.FileUtil;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.Result;
import io.minio.StatObjectArgs;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Item;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.LinkedList;
import java.util.List;

public class MinIOUtil {
    Logger log = LoggerFactory.getLogger(MinIOUtil.class);

    private final String endpoint;
    private final String accessKey;
    private final String secretKey;

    private static MinioClient client;

    public MinIOUtil(String endpoint, String accessKey, String secretKey) {
        this.endpoint = endpoint;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
    }

    public static void main(String[] args) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinIOUtil cli = new MinIOUtil("http://192.168.185.9:9000", "northpool", "piesat@123");
        List<FileObject> fileObjects = cli.treeObjects("stp-minio", "nodes/supermap/");
        download(cli, "stp-minio", "D:/temp/stp-minio", fileObjects);
        System.out.println(fileObjects.size());
    }

    public static void download(MinIOUtil cli, String bucket, String prefix, List<FileObject> fileObjects) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        if (fileObjects == null || fileObjects.isEmpty()) {
            return;
        }
        for (FileObject fileObject : fileObjects) {
            if (fileObject.isDir) {
                String dirPath = File.separator + fileObject.getObjectName();
                FileUtil.mkdir(prefix + dirPath.replace(":", "@"));
                download(cli, bucket, prefix, fileObject.getChildren());
            } else {
                InputStream inputStream = cli.getObject(bucket, fileObject.getObjectName());
                String fullFilePath = File.separator + fileObject.getObjectName();
                FileUtil.writeFromStream(inputStream, prefix + fullFilePath.replace(":", "@"));
            }
        }
    }

    private MinioClient getClient() {
        if (client == null) {
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(Duration.ofSeconds(20)) // 设置连接超时时间为10秒
                    .readTimeout(Duration.ofSeconds(30)) // 设置读取超时时间为30秒
                    .build();

            client = MinioClient.builder()
                    .endpoint(this.endpoint)
                    .credentials(this.accessKey, this.secretKey)
                    .httpClient(httpClient)
                    .build();
        }
        return client;
    }

    public boolean bucketExists(String bucketName) {
        try {
            return getClient().bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(bucketName)
                            .build());
        } catch (Exception ex) {
            log.error("bucketExists异常", ex);
            return false;
        }
    }

    public boolean objectExists(String bucketName, String objName) {
        try {
            getClient().statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objName)
                            .build());
            return true;
        } catch (ErrorResponseException e) {
            if (e.errorResponse().code().equals("NoSuchKey")) {
                return false;
            } else {
                log.error("objectExists异常", e);
                return false;
            }
        } catch (Exception ex) {
            log.error("objectExists异常", ex);
            return false;
        }
    }

    public boolean makeBucket(String bucketName) {
        try {
            getClient().makeBucket(
                    MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .build());
        } catch (Exception ex) {
            log.error("makeBucket异常", ex);
            return false;
        }
        log.debug("makeBucket成功:{}", bucketName);
        return true;
    }

    public boolean mkdir(String bucketName, String dir) {
        try {
            getClient().putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(dir)
                            .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
                            .build());
        } catch (Exception ex) {
            log.error("mkdir异常", ex);
            return false;
        }
        return true;
    }

    public boolean putObject(String bucketName, String objName, InputStream inputStream) {
        return putObject(bucketName, objName, inputStream, "application/octet-stream");
    }

    public boolean putObject(String bucketName, String objName, InputStream inputStream, String contentType) {
        try {
            getClient().putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objName)
                            .contentType(contentType)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
        } catch (Exception ex) {
            log.error("putObject异常,bucketName:{},objName:{}", bucketName, objName, ex);
            return false;
        }
        log.debug("putObject成功,bucketName:{},objName:{}", bucketName, objName);
        return true;
    }

    public List<FileObject> treeObjects(String bucketName, String prefix) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        List<FileObject> list = new LinkedList<>();
        for (Result<Item> result : listObjects(bucketName, prefix)) {
            Item item = result.get();
            // 过滤掉隐藏文件
            String objectName = item.objectName();

            FileObject fileObject = new FileObject();
            fileObject.setDir(item.isDir());
            fileObject.setObjectName(objectName);
            fileObject.setUrl(String.format("%s/%s/%s", this.endpoint, bucketName, objectName));
            String fileName;
            if (item.isDir()) {
                int index = Math.max(0, objectName.substring(0, objectName.length() - 1).lastIndexOf("/"));
                fileName = objectName.substring(index, objectName.length() - 1);
                fileObject.setChildren(treeObjects(bucketName, objectName));
            } else {
                int index = item.objectName().lastIndexOf("/");
                fileName = objectName.substring(index + 1);
                fileObject.setLastModified(item.lastModified());
            }
            // 过滤掉隐藏文件夹
            if (!fileName.startsWith(".")) {
                fileObject.setFileName(fileName);
                list.add(fileObject);
            }
        }
        return list;
    }

    public Iterable<Result<Item>> listObjects(String bucketName, String prefix) {
        return getClient().listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).build());
    }

    public InputStream getObject(String bucketName, String objName) throws ServerException, InsufficientDataException,
            ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidResponseException, XmlParserException, InternalException {
        return getClient().getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objName)
                        .build());

    }

    public boolean removeObject(String bucketName, String objName) {
        try {
            getClient().removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objName)
                            .build());
        } catch (Exception ex) {
            log.error("removeObject异常", ex);
            return false;
        }
        return true;
    }

}